﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Linq.Expressions;
using System.Reflection;

namespace Marmat.Forms
{
    /// <summary>
    /// 提供一个用于快速检索的搜索控件
    /// </summary>
    public partial class SearchControl : System.Windows.Forms.UserControl
    {

        /// <summary>
        /// 构建一个SearchControl实例
        /// </summary>
        public SearchControl()
        {
            InitializeComponent();

            //初始化属性
            this.label1.BackColor = Color.Transparent;
            this.BackColor = Color.White;
            this.searchTextBox1.Properties.NullValuePrompt = "搜索...";
            this.Font = new System.Drawing.Font("微软雅黑", 9F);
            //this.OnFontChanged(EventArgs.Empty);
        }

        /// <summary>
        ///  获取或设置控件内的空白。
        /// </summary>
        [Browsable(false)]
        [System.ComponentModel.DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new Padding Padding { get => base.Padding; set => base.Padding = value; }

        [DefaultValue(false)]
        public override bool AutoSize { get => base.AutoSize; set => base.AutoSize = value; }

        /// <summary>
        /// 获取或设置是否显示搜索按钮。
        /// </summary>
        [DefaultValue(true)]
        public bool ShowSearchButton { get => this.label2.Visible; set => this.label2.Visible = value; }

        /// <summary>
        /// 获取或设置搜索框的提示文本。
        /// </summary>
        [DefaultValue("搜索...")]
        public string HintLabel { get => this.searchTextBox1.Properties.NullValuePrompt; set => this.searchTextBox1.Properties.NullValuePrompt = value; }

        /// <summary>
        /// 获取或设置筛选组件中的编辑的值对象。
        /// </summary>
        [DefaultValue(null)]
        public string EditValue
        {
            get
            {
                if (this.ValueCollection.Count == 0)
                    return null;
                return Newtonsoft.Json.JsonConvert.SerializeObject(this.ValueCollection);
            }
            set
            {
                if (string.IsNullOrEmpty(value))
                    this.ValueCollection.Clear();
                else
                    this.ValueCollection.AddRange(Newtonsoft.Json.JsonConvert.DeserializeObject<ValueItem[]>(value));
            }
        }

        /// <summary>
        /// 获取搜索控件的值。
        /// </summary>
        [Browsable(false)]
        public string Value
        {
            get
            {
                StringBuilder builder = new StringBuilder();
                foreach (var value in this.ValueCollection)
                {
                    var column = this.Columns.FirstOrDefault(x => x.Name == value.name);
                    if (column is null) continue;

                    if (column is Field field)
                    {
                        StringBuilder builderItem = new StringBuilder();
                        Type type = field.DataType;
                        foreach (string filter in value.args)
                        {
                            string item = null;
                            if (type == typeof(string))
                                item = $"{field.Name} Like {$"'%{filter.Replace("'", "''")}%'"}";
                            else if (type == typeof(int) || type == typeof(decimal) || type == typeof(float) || type == typeof(double) || type == typeof(long) || type == typeof(float))
                                item = field.Name + " = " + filter;
                            else if (type == typeof(DateTime))
                                item = $"DateDiff(dd,{field.Name},'{DateTime.Parse(filter):yyyy-MM-dd}')=0";
                            else if (type == typeof(bool))
                            {
                                if (filter == "0" || filter.ToLower() == "false" || filter == "否")
                                    item = $"{field.Name} = 0";
                                else if (filter == "1" || filter.ToLower() == "true" || filter == "是")
                                    item = $"{field.Name} = 1";
                            }
                            if (!string.IsNullOrEmpty(item) && builderItem.Length > 0)
                                builderItem.Append(" Or ");
                            builderItem.Append(item);
                        }

                        string collection = $"({builderItem})";
                        if (!string.IsNullOrEmpty(field.Condition))
                            collection = field.Condition.Replace("{0}", collection);
                        if (builder.Length > 0)
                        {
                            builder.Append(" And ");
                        }
                        builder.Append(collection);
                    }
                    else if (column is Group group)
                    {
                        string collection = "(" + group.Domain + ")";
                        if (builder.Length > 0)
                        {
                            builder.Append(" And ");
                        }
                        builder.Append(collection);
                    }
                }
                return builder?.ToString();
            }
        }


        /// <summary>
        /// 字段集合
        /// </summary>
        [DefaultValue(null)]
        public FieldCollection Columns { get; } = new FieldCollection();

        /// <summary>
        /// 值变化后触发
        /// </summary>
        public event EventHandler ValueChanged;

        #region 私有属性

        private SearchDropDownPanel _panel;
        /// <summary>
        /// 下拉面板
        /// </summary>
        private SearchDropDownPanel Panel
        {
            get
            {
                if (this._panel is null)
                    this._panel = new SearchDropDownPanel(this);
                return this._panel;
            }
        }

        private System.Windows.Forms.ToolStripControlHost ToolStripControlHost { get; set; }

        private System.Windows.Forms.ToolStripDropDown m_toolStripDropDown;
        private System.Windows.Forms.ToolStripDropDown ToolStripDropDown
        {
            get
            {
                if (m_toolStripDropDown is null)
                {
                    m_toolStripDropDown = new System.Windows.Forms.ToolStripDropDown();
                    m_toolStripDropDown.AutoClose = false;
                    m_toolStripDropDown.DropShadowEnabled = false;
                    m_toolStripDropDown.Padding = new Padding(1, 0, 1, 0);
                }
                return m_toolStripDropDown;
            }
        }

        /// <summary>
        /// 关闭按钮的矩形字典
        /// </summary>
        private Dictionary<ValueItem, Rectangle> CloseRectangles = new Dictionary<ValueItem, Rectangle>();
        /// <summary>
        /// 关闭按钮的矩形字典
        /// </summary>
        private Dictionary<ValueItem, Rectangle> ItemRectangles = new Dictionary<ValueItem, Rectangle>();
        #endregion

        /// <summary>
        /// 搜索控件的值
        /// </summary>
        public List<ValueItem> ValueCollection { get; } = new List<ValueItem>();

        /// <summary>
        /// 添加条件。
        /// </summary>
        /// <param name="columns"></param>
        public void AddCondition<TEntity>(params string[] columns)
        {
            MemberInfo[] members = typeof(TEntity).GetMembers().Where(x => x is FieldInfo or PropertyInfo).ToArray();
            foreach (var columnName in columns)
            {
                MemberInfo member = members.First(x => x.Name == columnName);
                Field item = new();
                item.Name = member.Name;
                item.Caption = member.Name;
                if (member is PropertyInfo property)
                    item.DataType = property.PropertyType;
                else if (member is FieldInfo field)
                    item.DataType = field.FieldType;
                DescriptionAttribute description = member.GetCustomAttribute<DescriptionAttribute>();
                if (description != null)
                    item.Caption = description.Description;
                this.Columns.Add(item);
            }
        }

        /// <summary>
        /// 添加条件。
        /// </summary>
        /// <param name="expression"></param>
        public void AddCondition<TEntity>(Expression<Func<TEntity, object>> expression)
        {
            List<string> list = new List<string>();
            if (expression.Body is System.Linq.Expressions.UnaryExpression unary)
            {
                if (unary.Operand is System.Linq.Expressions.MemberExpression member)
                    list.Add(member.Member.Name);
            }
            else if (expression.Body is System.Linq.Expressions.MemberExpression member)
                list.Add(member.Member.Name);
            else if (expression.Body is System.Linq.Expressions.NewExpression newExpression)
            {
                foreach (var item in newExpression.Arguments)
                {
                    if (item is System.Linq.Expressions.MemberExpression item_member)
                        list.Add(item_member.Member.Name);
                }
            }
            AddCondition<TEntity>(list.ToArray());
        }


        public void AddValue(string groupName)
        {
            var group = this.Columns.OfType<Group>().FirstOrDefault(x => x.Name == groupName);
            if (group is null)
                throw new NullReferenceException($"没有找到指定的筛选条件 [{groupName}] 。");
            this.AddValue(group);
        }

        public void AddValue(SearchControl.Group group)
        {
            this.ValueCollection.Add(new ValueItem() { name = group.Name });
            this.RaiseEventValueChanged(EventArgs.Empty);
        }

        protected void RaiseEventValueChanged(EventArgs e)
        {
            this.Invalidate();
            Application.DoEvents();
            this.ValueChanged?.Invoke(this, EventArgs.Empty);
        }

        /// <summary>
        /// 添加条件
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="values"></param>
        public void AddValue(string fieldName, params object[] values)
        {
            var field = this.Columns.OfType<Field>().First(x => x.Name == fieldName);
            this.AddValue(field, values);
        }

        /// <summary>
        /// 添加条件
        /// </summary>
        /// <param name="field"></param>
        /// <param name="values"></param>
        public void AddValue(ISearchControlField field, params object[] values)
        {
            this.Text = string.Empty;
            var filter = this.ValueCollection.FirstOrDefault(x => x.name == field.Name);
            if (filter is null)
            {
                filter = new ValueItem() { name = field.Name };
                this.ValueCollection.Add(filter);
                if (field is Field)
                    filter.args = values;
            }
            else if (field is Field)
            {
                filter.args = filter.args.Union(values).ToArray();
            }
            this.RaiseEventValueChanged(EventArgs.Empty);
        }

        /// <summary>
        /// 显示下拉窗体
        /// </summary>
        public void ShowDropDownForm()
        {
            if (this.ToolStripDropDown.Items.Count == 0 && this.Panel != null && ToolStripDropDown.IsDisposed == false)
            {
                this.ToolStripDropDown.Dock = DockStyle.Fill;
                this.ToolStripControlHost = new System.Windows.Forms.ToolStripControlHost(this.Panel);
                ToolStripControlHost.AutoSize = false;
                ToolStripDropDown.Items.Add(ToolStripControlHost);
                ToolStripDropDown.Tag = this;
            }
            if (this.Panel != null)
            {
                ToolStripControlHost.Width = this.Width - 2;
                m_toolStripDropDown.Show(this, 0, this.Height + 2);
            }
        }

        [DefaultValue(typeof(Font), "微软雅黑, 9pt")]
        public override Font Font
        {
            get => base.Font; set
            {
                this.searchTextBox1.Font = value;
                this.Height = this.searchTextBox1.Height + 2;
                base.Font = value;
            }
        }

        [DefaultValue(typeof(Color), nameof(Color.White))]
        public override Color BackColor { get => base.BackColor; set => base.BackColor = value; }


        /// <summary>
        /// 关闭下拉窗口
        /// </summary>
        public void CloseDropDown()
        {
            if (this.ToolStripDropDown != null)
            {
                this.ToolStripDropDown.Close();
            }
        }

        /// <summary>
        /// 获取或设置编辑器的文本。
        /// </summary>
        public override string Text { get => this.searchTextBox1.Text; set => this.searchTextBox1.Text = value; }

        protected override Size DefaultSize => new Size(200, 20);

        /// <summary>
        /// 引发 System.Windows.Forms.Control.FontChanged 事件。
        /// </summary>
        /// <param name="e"></param>
        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
            this.searchTextBox1.Font = this.Font;

            if (this.Panel != null)
                this.Panel.Font = this.Font;
            //this.Height = this.FontHeight + 7;
        }

        private void searchTextBox1_TextChanged(object sender, EventArgs e)
        {
            if (this.Columns.Count == 0)
                return;
            if (string.IsNullOrEmpty(this.searchTextBox1.Text))
            {
                this.CloseDropDown();
            }
            else
            {
                this.Panel.listBox1.Items.Clear();
                string value = this.Text;
                foreach (ISearchControlField item in this.Columns)
                {
                    if (item.ConvertValue(value, out _))
                    {
                        FieldItem field = new FieldItem();
                        field.Field = item;
                        field.Value = value;
                        this.Panel.listBox1.Items.Add(field);
                    }
                }
                if (this.Panel.listBox1.Items.Count > 0)
                {
                    this.Panel.listBox1.SelectedIndex = 0;
                    this.ShowDropDownForm();
                    int itemHeight = 22;
                    this.ToolStripControlHost.Height = itemHeight * this.Panel.listBox1.Items.Count;
                }
            }
        }

        private void searchTextBox1_Leave(object sender, EventArgs e)
        {
            this.searchTextBox1.EditValue = null;
            this.CloseDropDown();
        }

        private void searchTextBox1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Down)
            {
                if (this.m_toolStripDropDown != null && this.m_toolStripDropDown.Visible)
                {
                    this.Panel.listBox1.SelectedIndex = Math.Min(this.Panel.listBox1.SelectedIndex + 1, this.Panel.listBox1.Items.Count - 1);
                    e.Handled = true;
                }
            }
            else if (e.KeyCode == Keys.Up)
            {
                if (this.m_toolStripDropDown != null && this.m_toolStripDropDown.Visible)
                {
                    this.Panel.listBox1.SelectedIndex = Math.Max(0, this.Panel.listBox1.SelectedIndex - 1);
                    e.Handled = true;
                }
            }
            else if (e.KeyCode == Keys.Enter)
            {
                if (this.m_toolStripDropDown != null && this.m_toolStripDropDown.Visible)
                {
                    if (this.m_toolStripDropDown.Visible && this.Panel.listBox1.SelectedItem != null && this.Panel.listBox1.SelectedItem is FieldItem field)
                        this.AddValue(field.Field, field.Value);
                    this.CloseDropDown();
                }
                else
                {
                    this.RaiseEventValueChanged(EventArgs.Empty);
                }
            }
            else if (e.KeyCode == Keys.Escape)
            {
                this.searchTextBox1.Text = string.Empty;
                this.CloseDropDown();
            }
            else if (e.KeyCode == Keys.Back && string.IsNullOrEmpty(this.Text))
            {
                if (this.ValueCollection.Count > 0)
                {
                    ValueItem value = this.ValueCollection[this.ValueCollection.Count - 1];
                    this.ValueCollection.Remove(value);
                    this.RaiseEventValueChanged(EventArgs.Empty);

                    if (this.CloseRectangles.ContainsKey(value))
                    {
                        this.CloseRectangles.Remove(value);
                    }
                    this.label1.Invalidate();
                }
            }
        }

        /// <summary>
        /// 引发 System.Windows.Forms.Control.Paint 事件。
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            SolidBrush brush = new SolidBrush(this.BackColor);
            e.Graphics.FillRectangle(brush, new RectangleF(0, 0, this.Width, this.Height));
            Pen pen = new Pen(Color.FromArgb(180, 191, 191, 191));
            e.Graphics.DrawRectangle(pen, new Rectangle(0, 0, this.Width - 1, this.Height - 1));
        }

        /// <summary>
        /// 绘制文本框样式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            SolidBrush brush = new SolidBrush(this.searchTextBox1.BackColor);
            Pen pen = new Pen(Color.FromArgb(180, 191, 191, 191));


            e.Graphics.FillRectangle(brush, new Rectangle(0, 0, this.Width, this.Height));
            e.Graphics.DrawRectangle(pen, new Rectangle(0, 0, this.Width - 1, this.Height - 1));
            //System.Windows.Forms.TextBoxRenderer.DrawTextBox(e.Graphics, new Rectangle(0, 0, this.Width, this.Height), System.Windows.Forms.VisualStyles.TextBoxState.Selected);
        }

        private void label1_Paint(object sender, PaintEventArgs e)
        {
            this.CloseRectangles.Clear();
            this.ItemRectangles.Clear();
            int left = 2;
            int top = 2;
            int height = this.label1.Height - 5;
            foreach (ValueItem item in this.ValueCollection)
            {
                var column = this.Columns.FirstOrDefault(x => x.Name == item.name);
                string field = string.IsNullOrEmpty(column.Caption) ? column.Name : column.Caption;
                string vals = item.ToString();
                int item_width = 0;
                Size size;
                Image img;
                int x = 0, y = 0;
                Rectangle rect = Rectangle.Empty;
                SolidBrush brush = new SolidBrush(Color.FromArgb(119, 119, 119));

                //绘制字段名
                if (column is Group)
                {
                    vals = field;
                    img = Properties.Resources.SearchControl_filter;
                    x = left + item_width;
                    rect = new Rectangle(x, top, img.Width + 4, height);
                    e.Graphics.FillRectangle(brush, rect);
                    item_width += rect.Width;

                    y = (int)Math.Ceiling((double)(rect.Height - img.Height) / 2);
                    rect = new Rectangle(x + 2, rect.Y + y, img.Width, img.Height);
                    e.Graphics.DrawImage(img, rect, new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);

                }
                else if (column is Field)
                {
                    Font font = new Font(this.Font.Name, this.Font.Size - 1);
                    size = TextRenderer.MeasureText(e.Graphics, field, font);
                    rect = new Rectangle(left, top, size.Width, height);
                    brush = new SolidBrush(Color.FromArgb(119, 119, 119));
                    e.Graphics.FillRectangle(brush, rect);
                    TextRenderer.DrawText(e.Graphics, field, font, rect, Color.White, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.NoClipping);
                    item_width += size.Width;
                }

                //绘制字段值
                size = TextRenderer.MeasureText(e.Graphics, vals, this.Font);
                rect = new Rectangle(left + item_width, top, size.Width, height);
                brush = new SolidBrush(Color.FromArgb(233, 233, 241));
                e.Graphics.FillRectangle(brush, rect);

                rect = new Rectangle(left + item_width, top, size.Width, height);
                TextRenderer.DrawText(e.Graphics, vals, this.Font, rect, Color.Black, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPadding);
                item_width += size.Width;

                //绘制删除按钮的矩形
                img = Properties.Resources.SearchControl_delete;
                rect = new Rectangle(left + item_width, top, img.Width + 3, height);
                e.Graphics.FillRectangle(brush, rect);

                x = left + item_width;
                y = (int)Math.Ceiling((double)(this.label1.Height - img.Height) / 2);

                rect = new Rectangle(x, y, img.Width, img.Height);
                e.Graphics.DrawImage(img, rect, new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);

                if (!this.CloseRectangles.ContainsKey(item))
                    this.CloseRectangles.Add(item, rect);
                else
                    this.CloseRectangles[item] = rect;

                //绘制项边框
                item_width += img.Width + 3;
                Pen pen = new Pen(Color.FromArgb(119, 119, 119));
                rect = new Rectangle(left, top, item_width - 1, height);
                e.Graphics.DrawRectangle(pen, rect);
                ItemRectangles[item] = rect;
                left += item_width + 1;
            }
            if (this.ValueCollection.Count == 0) left = 1;
            if (this.label1.Width != left)
            {
                this.label1.Width = left;
            }
        }

        private void label1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                var item = this.ItemRectangles.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
                var close_item = this.CloseRectangles.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
                if (close_item != null)
                {
                    this.ValueCollection.Remove(close_item);
                    this.label1.Invalidate();
                    this.RaiseEventValueChanged(EventArgs.Empty);
                }
                else if (item != null)
                {
                    //if (item is Field field)
                    //{
                    //    if (field.DataType == typeof(string))
                    //    {
                    //        string value = string.Join("\r\n", field.ValueCollection);
                    //        if (InputRemark.Show(ref value, "请输入您要修改的值:"))
                    //        {
                    //            field.ValueCollection = value.Split("\r\n".ToCharArray()).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList();
                    //            this.label1.Invalidate();
                    //            this.RaiseEventValueChanged(EventArgs.Empty);
                    //        }
                    //    }
                    //}
                }
            }
        }

        private void label1_MouseMove(object sender, MouseEventArgs e)
        {
            if (this.Columns.Count == 0)
                return;
            var item = this.CloseRectangles.FirstOrDefault(x => x.Value.Contains(e.Location)).Key;
            this.Cursor = (item == null) ? Cursors.Default : Cursors.Hand;
        }

        private void panel1_Resize(object sender, EventArgs e)
        {
            this.Invalidate();
        }

        private void label2_Click(object sender, EventArgs e)
        {
            this.RaiseEventValueChanged(EventArgs.Empty);
        }

        /// <summary>
        /// 通过指定列名添加对应的搜索集合。
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public void ColumnBuilder<T>(Expression<Func<T, object>> expression)
        {
            this.Columns.Clear();
            if (expression.Body is System.Linq.Expressions.UnaryExpression unary)
            {
                if (unary.Operand is MemberExpression member)
                    Columns.Add(this.GetFieldCollection(member.Member));
            }
            else if (expression.Body is MemberExpression member)
                Columns.Add(this.GetFieldCollection(member.Member));
            else if (expression.Body is NewExpression newExpression)
            {
                foreach (var item in newExpression.Arguments)
                {
                    if (item is System.Linq.Expressions.MemberExpression item_member)
                        Columns.Add(this.GetFieldCollection(item_member.Member));
                }
            }
        }

        /// <summary>
        /// 根据System.Reflection.MemberInfo获取Field
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        private Field GetFieldCollection(System.Reflection.MemberInfo member)
        {
            var result = new Field();
            if (member is System.Reflection.FieldInfo field)
            {
                result.Name = field.Name;
                result.DataType = field.FieldType;
                result.Caption = field.Name;
            }
            else if (member is System.Reflection.PropertyInfo property)
            {
                result.Name = property.Name;
                result.DataType = property.PropertyType;
                result.Caption = property.Name;
            }
            DescriptionAttribute description = member.GetCustomAttribute<DescriptionAttribute>();
            if (description != null)
                result.Caption = description.Description;
            return result;
        }

    }
}
